﻿#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.GamerServices;
using Lidgren.Network;
#endregion

namespace GoodSyncGame
{
    public enum Headers
    {
        NewData,
        SyncData
    }

    public class Player
    {
        public int Key { get; set; }
        public string Name { get; set; }
        public float posX { get; set; }

        public Player()
        {

        }

        public void Draw(Texture2D texture, SpriteBatch spriteBatch)
        {
            spriteBatch.Draw(texture, new Vector2(posX, 60), Color.White);
        }
    }

    public class Game1 : Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        NetClient Client;
        Dictionary<int, Player> Players = new Dictionary<int, Player>();
        Player Me;
        const NetDeliveryMethod DeliverType = NetDeliveryMethod.UnreliableSequenced;
        Texture2D Texture;

        public Game1()
            : base()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            NetPeerConfiguration config = new NetPeerConfiguration("MyGame");
            Client = new NetClient(config);
            Client.Start();
            Client.Connect("127.0.0.1", 7777);

            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            Texture = Content.Load<Texture2D>("player.png");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        private void BeginSendData(int playerkey, Headers header, out NetOutgoingMessage outmsg)
        {
            outmsg = Client.CreateMessage();
            outmsg.Write(playerkey);
            outmsg.Write((byte)header);
        }

        private void EndSendData(NetOutgoingMessage outmsg)
        {
            Client.SendMessage(outmsg, DeliverType);
        }

        NetIncomingMessage incmsg;
        NetOutgoingMessage outmsg;
        int playerkey;
        Player player;

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
                Exit();

            if (Me != null)
            {
                float moveX = 0f;

                if (Keyboard.GetState().IsKeyDown(Keys.Left))
                    moveX = -1f;
                else if (Keyboard.GetState().IsKeyDown(Keys.Right))
                    moveX = 1f;
                else
                    moveX = 0;

                if (moveX != 0)
                {
                    Me.posX += moveX;
                    BeginSendData(Me.Key, Headers.SyncData, out outmsg);
                    outmsg.Write(moveX);
                    EndSendData(outmsg);
                }
            }

            if ((incmsg = Client.ReadMessage()) != null)
            {
                switch (incmsg.MessageType)
                {
                    case NetIncomingMessageType.Data:

                        playerkey = incmsg.ReadInt32();
                        Headers header = (Headers)incmsg.ReadByte();

                        switch (header)
                        {
                            case Headers.NewData:

                                if (Me == null)
                                {
                                    int playerscount = incmsg.ReadInt32();

                                    if (playerscount <= 1)
                                    {
                                        player = new Player();
                                        incmsg.ReadAllProperties(player);
                                        Players.Add(player.Key, player);
                                        Me = Players[playerkey];
                                    }
                                    else
                                    {
                                        for (int i = 0; i < playerscount; i++)
                                        {
                                            player = new Player();
                                            incmsg.ReadAllProperties(player);
                                            Players.Add(player.Key, player);
                                        }

                                        Me = Players[playerkey];
                                    }
                                }
                                else
                                {
                                    player = new Player();
                                    incmsg.ReadAllProperties(player);
                                    Players.Add(player.Key, player);
                                }

                                break;

                            case Headers.SyncData:

                                float xPos = incmsg.ReadFloat();
                                Players[playerkey].posX += xPos;
                                break;
                        }

                        break;
                }

                Client.Recycle(incmsg);
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();
            foreach (KeyValuePair<int, Player> otherplayer in Players)
                otherplayer.Value.Draw(Texture, spriteBatch);
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
